package codebreaker.servidor;

import codebreaker.protocolo.Constantes;
import codebreaker.protocolo.PedidoConexao;
import codebreaker.protocolo.RespostaConexao;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Objeto que fica escutando a porta de negociacao da conexao dos
 * jogadores com o servidor code breaker.
 * O negociador é responsavel por receber pedidos de conexao, analizar
 * os pedidos, e enviar a resposta. Se o pedido for aceito o negociador
 * deve alocar uma porta para o jogador se conectar ao servidor definitivamente.
 *
 * @author Rodrigo
 */
public class Negociador implements Runnable {
    
    private int porta;
    
    /**
     * Cria um negociador, que escutara na porta 9999
     * por clientes que desejam se conectar ao servidor
     * @param porta Porta de escuta
     */
    public Negociador(int porta) {
	this.porta = porta;
    }
    
    /**
     * Cria um negociador, que escutara na porta 9999
     * por clientes que desejam se conectar ao servidor
     */
    public Negociador() {
	this.porta = codebreaker.protocolo.Constantes.PORTA_NEGOCIACAO;
    }
    
    public void run() {
	try {
	    byte buffer[] = new byte[128];
	    ServerSocket serverSocket = new ServerSocket(porta);
	    
	    /* laco infinito */
	    while (true) {
		
		/* aceita conexao */
		System.out.println("Negociador: Aguardando conexao de cliente...");
		Socket socket = serverSocket.accept();
		
		/* seta entrada e saida */
		OutputStream out = socket.getOutputStream();
		InputStream in = socket.getInputStream();
		
		/* Le pacote de pedido de requisicao */
		if (in.read() == PedidoConexao.ID) {
		    
		    /* le restante do pacote */
		    in.read(buffer);
		    /* cria pacote, atravez do protocolo */
		    PedidoConexao pedidoConexao = new PedidoConexao(buffer, false);
		    
		    System.out.println("Negociacao: Recebendo pedido de conexao de " + 
			    socket.getInetAddress() + ", " + pedidoConexao.getNome());
		    
		    /* verifica se nao estourou limite de jogadores online */
		    if (Main.nucleo.numeroJogadores() >= Constantes.MAXIMO_JOGADORES) {
			System.err.println("Negociacao: Pedido negado: numero limite atingido: " +
				Constantes.MAXIMO_JOGADORES);
			
			/* envia resposta */
			RespostaConexao resposta = new RespostaConexao(RespostaConexao.LIMITE_JOGADORES, 0, (byte)0);
			out.write(resposta.toBytes(true));
			
			out.close();
			in.close();
			socket.close();
			continue;
		    }
		    
		    /* verifica se nome nao conflita */
		    if (Main.nucleo.nomeExiste(pedidoConexao.getNome())) {
			System.err.println("Negociacao: Pedido negado: nome ja existe: " + 
				pedidoConexao.getNome());
			
			/* envia resposta */
			RespostaConexao resposta = new RespostaConexao(RespostaConexao.NOME_CONFLITANTE, 0, (byte)0);
			out.write(resposta.toBytes(true));
			
			out.close();
			in.close();
			socket.close();
			continue;
		    }
		    
		    /* ok, adiciona jogador ao sistema */
		    int porta = Main.nucleo.adicionarJogador(pedidoConexao.getNome());
		    System.out.println("Negociacao: Jogador " + pedidoConexao.getNome() + 
			    " foi aceito na porta " + porta);
		    
		    /* envia resposta */
		    RespostaConexao resposta = new RespostaConexao(RespostaConexao.CONEXAO_ACEITA, 
			    porta, (byte)(porta-Main.nucleo.PORTA_BASE));
		    out.write(resposta.toBytes(true));
		    
		    out.close();
		    in.close();
		    socket.close();
		    continue;
		} else {
		    System.err.println(
			    "Negociacao: Pacote invalido recebido pela porta" +
			    porta + " do cliente " + socket.getRemoteSocketAddress());
		    
		    out.close();
		    in.close();
		    socket.close();
		    continue;
		}
	    }
	     
	} catch (IOException ex) {
	    ex.printStackTrace();
	}
    }
    
}